<?php
/**
 * 命名：腾讯的id命名为qqwb_id，如果在token中，一律命名为uid
 */
class data_qqwb extends data_social {
	private $akey;
	private $skey;
	private $authorize_url;
	private $token_url;
	private $api_url;
	public function __construct() {
		$this->akey			= QQWB_AKEY;
		$this->skey			= QQWB_SKEY;
		$this->authorize_url	= QQWB_AUTHORIZE_URL;
		$this->token_url		= QQWB_TOKEN_URL;
		$this->api_url		= QQWB_API_URL;
		parent::__construct();
	}
	/**
	 * 获取登陆的url
	 * @return String
	 */
	public function authorize_url($flag = 'iframe/setting') {
		if ($this->akey) {
			$params = array (
				'client_id'		=> $this->akey,
				'redirect_uri'	=> data_social::make_callback_url(),
				'response_type'	=> 'code',
				'state'		=> data_social::make_state('qqwb', $flag),
				'wap'			=> false,
			);
			return $this->authorize_url . '?' . http_build_query($params);
		} else return false;
	}
	/**
	 * 
	 * @param type $code
	 * @return array (
	 *		qqwb_id, access_token, refresh_token, ctime, expires_in, name, nick
	 *	)
	 */
	public function code_2_token($code) {
		if ($this->akey and $code) {
			$api_param = array (
				'client_id'		=> $this->akey,
				'client_secret'	=> $this->skey,
				'redirect_uri'	=> data_social::make_callback_url(),			// 注意callback url一定要一致
				'grant_type'		=> 'authorization_code',
				'code'		=> $code
			);
			
			parse_str($this->request($this->token_url, $api_param), $api_result);		// 使用code换取token (access_token, refresh_token, expires_in, openid, name, nick, state)

			if ($api_result['access_token'] and $api_result['expires_in']>0 and $api_result['openid'] and $api_result['nick']) {
				$token = array (
					'uid'			=> $api_result['openid'],
					'access_token'	=> $api_result['access_token'],
					'refresh_token'	=> $api_result['refresh_token'],
					'ctime'		=> time(),
					'expires_in'		=> $api_result['expires_in'],
					'name'		=> $api_result['name'],
					'nick'			=> $api_result['nick']
				);
				return $token;
			} else {
				new data_error_devel('腾讯微博code to token错误', array('code'=>$code, 'api_result'=>$api_result), __CLASS__, __FUNCTION__, __LINE__);
				return false;
			}
		} else return false;
	}

	public function token_valid_or_refresh_into_db ($token_from_db) {
		if (isset($token_from_db['uid']) and isset($token_from_db['access_token']) and isset($token_from_db['refresh_token'])) {
			// 检查token是否有效
			if ($this->is_token_valid($token_from_db['uid'], $token_from_db['access_token'])) {	// 这里是用user/info来判断是否有效，返回值中不包括与token相关的信息
				return $token_from_db;
			} else {
				// 刷新token
				$api_param = array (
					'client_id'		=> $this->akey,
					'refresh_token'	=> $token_from_db['refresh_token'],
					'grant_type'		=> 'refresh_token',
				);
				parse_str($this->request($this->token_url, $api_param), $api_result);		// 刷新后返回的token结构与原来一样 (access_token, refresh_token, expires_in, openid, name, nick, state)
				if ($api_result ['access_token'] and $api_result ['expires_in']>0) {
					$token_to_db = array (
						'uid'			=> $token_from_db['uid'],
						'access_token'	=> $api_result['access_token'],
						'refresh_token'	=> $api_result['refresh_token'],
						'ctime'		=> time(),
						'expires_in'		=> $api_result['expires_in'],
					);
					$this->save_token('qqwb', $token_to_db);			// 写入数据库
					// 把name和nick添加到返回结果中
					$token_to_db['name']		= $api_result['name'];
					$token_to_db['nick']		= $api_result['nick'];
					return $token_to_db;
				} else {
					$token_to_db = array (
						'uid'		=> $token_from_db['uid'],
						'ctime'	=> time()
					);
					$this->save_token('qqwb', $token_to_db);			// 写入数据库
					new data_error_devel ( '腾讯refresh token失败', array ('token_from_db' => $token_from_db, 'api_result' => $api_result), __CLASS__, __FUNCTION__, __LINE__ );
					return false;
				}
			}
		} else return false;
	}
	/**
	 * 获取自己的信息，用这个来验证token是否过期
	 * 
	 * @param type $qqwb_id
	 * @param type $access_token
	 * @return boolean
	 */
	private function is_token_valid ($qqwb_id, $access_token) {
		if ($this->get_user_info($access_token, $qqwb_id)) return true;
		else return false;
	}
	public function get_token ($uid) {
		$data_user = new data_user();
		$user = $data_user->one($uid);
		return $this->token_valid_or_refresh_into_db($user['qqwb_token']);
	}
	/**
	 * 
	 * @param type $uid
	 * @param type $token
	 * @return array (
	 *		array(qqwb_id, nickname, name)
	 * )
	 */
	public function get_friends($uid, $token='') {
		$uid = intval($uid);
		
		if ($uid) {
			if ($token) {
				$api_param = array(
					'fopenid'		=> $token['uid'], 
					'startindex'		=> 0, 
					'reqnum'		=> 30, 
					'install'		=> 0
				);
				$return = array();
				while (1) {
					$api_result = json_decode($this->api('friends/mutual_list', $token['access_token'], $token['uid'], $api_param), true);
					if ($api_result and $api_result['errcode']==0 and $api_result['data']['info']) {
						$return = array_merge($return, $api_result['data']['info']);
						if ($api_result['data']['hasnext'] == 0 and $api_result['data']['nextstartpos']) {
							$api_param['startindex'] = $api_result['data']['nextstartpos'];
						} else break;
					} else break;
				}
				if (count ( $return ) > 0) {
					$return = $this->friends_to_simple ( $return );
					$this->cache_write ( $uid, 'qqwb', 'friends', $return );
					return $return;
				} else {
					new data_error_devel('从腾讯微博API读取好友时出错，或者这个腾讯微博用户一个好友也没有？', array('uid'=>$uid, 'token'=>$token, 'api_result'=>$api_result), __CLASS__, __FUNCTION__, __LINE__);
				}
			} else {
				$cache = $this->cache_read ( $uid, 'qqwb', 'friends' );
				if ($cache) {
					return  $cache ;
				} else {
					return false;
				}
			}
		}
	}
	/**
	 * 简化数组
	 * @param array $array
	 * @return array = array (
	 *		array(qqwb_id, nickname, name)
	 *	)
	 */
	private function friends_to_simple ($array) {
		if (is_array ( $array )) {
			foreach ( $array as $k => $v ) {
				$array [$k] = array (
					'qqwb_id'	=> $v['openid'],
					'nick'		=> $v['nick'],			// 这个nick是显示的名字
					'name'	=> $v['name'],			// 这个name是用于构造@时用到的名字
				);
			}
			return $array;
		} else	return false;
	}
	/**
	 * 
	 * @param type $access_token
	 * @param type $qqwb_id
	 * @return array (uid, name, nick, ...)
	 */
	public function get_user_info ($access_token, $qqwb_id) {
		if ($access_token and $qqwb_id) {
			$api_result = json_decode($this->api('user/info', $access_token, $qqwb_id), true);
			if (is_array($api_result) and $api_result['errcode '] == 0 and $api_result['data']['name']) {
				// 简化数组
				return array (
					'qqwb_id'		=> $api_result['data']['openid'],
					'name'		=> $api_result['data']['name'],
					'nick'			=> $api_result['data']['nick'],
					'sex'			=> $api_result['data']['sex'],
					'province_code'	=> $api_result['data']['province_code'],
					'city_code'		=> $api_result['data']['city_code'],
				);
			} else {
				new data_error_devel('腾讯微博get user info错误', array('access_token'=>$access_token, 'qqwb_id'=>$qqwb_id, 'api_result'=>$api_result), __CLASS__, __FUNCTION__, __LINE__);
				return false;
			}
		}
	}
	public function post ($qqwb_id, $access_token, $content) {
		if (mb_strlen($content) > 140) {
			$content = mb_strcut($content, 0, 140);
		}
		$api_param = array (
			'content'	=> $content,
		);
		$api_result =  json_decode($this->api('t/add', $access_token, $qqwb_id, $api_param, 'POST'), true);
		if (is_array($api_result) and $api_result['errcode'] == 0 and $api_result['data']['id']) {
			return $api_result['data'];
		} else {
			return false;
			new data_error_devel ('腾讯微博发布API返回错误', array('qqwb_id'=>$qqwb_id, 'access_token'=>$access_token, 'content'=>$content, 'api_result'=>$api_result), __CLASS__, __FUNCTION__, __LINE__);
		} 
	}
	public function comment ($qqwb_id, $access_token, $reid, $content) {
		$api_param = array (
			'content'	=> $content,
			'reid'		=> $reid
		);
		$api_result =  json_decode($this->api('t/comment', $access_token, $qqwb_id, $api_param, 'POST'), true);
		if (is_array($api_result) and $api_result['errcode'] == 0 and $api_result['data']['id']) {
			return $api_result['data']['id'];
		} new data_error_devel ('腾讯微博发布API返回错误', array('qqwb_id'=>$qqwb_id, 'access_token'=>$access_token, 'content'=>$content, 'api_result'=>$api_result), __CLASS__, __FUNCTION__, __LINE__);
	}
	public function shorturl ($qqwb_id, $access_token, $url_long) {
		$api_param = array (
			'long_url'	=> $url_long
		);
		$api_result =  json_decode($this->api('short_url/shorten', $access_token, $qqwb_id, $api_param), true);
		if (is_array($api_result) and $api_result['errcode'] == 0 and $api_result['data']['short_url']) {
			return 'http://url.cn/'.$api_result['data']['short_url'];
		} else {
			return $url_long;
		}
	}
	
	public function get_user_info_by_openkey ($qqwb_id, $openkey) {
		if ($qqwb_id and $openkey) {
			$api_param['appid']		= $this->akey;
			$api_param['clientip']		= $this->getClientIp();
			$api_param['openid']		= $qqwb_id;
			$api_param['openkey']		= $openkey;
			$api_param['reqtime']		= time();
			$api_param['wbversion']	= 1;
			$api_param['sig']			= $this->makeSig('GET', '/user/info', $api_param, $this->skey);

			//请求接口
			$api_result = $this->request('http://open.t.qq.com/api/user/info', $api_param, 'GET', false);
			$api_result = preg_replace('/[^\x20-\xff]*/', "", $api_result);					//清除不可见字符
			$api_result = iconv("utf-8", "utf-8//ignore", $api_result);					//UTF-8转码，加上//ignore参数防止出错后的停止执行
			$api_result = json_decode($api_result, true);

			if (is_array($api_result) and $api_result['errcode'] == 0 and $api_result['data']['openid'] == $qqwb_id) {
				return array (
					'qqwb_id'	=> $api_result['data']['openid'],
					'nick'		=> $api_result['data']['nick'],
					'name'	=> $api_result['data']['name'],
				);
			} else return false;
		} else return false;
	}

	
	// -- 以下函数修改自腾讯API -------------------------------------------------------------------------------------
	/**
	 * 发起一个腾讯API请求
	 * @param $command 接口名称 如：t/add
	 * @param $access_token
	 * @param $openid 
	 * @param $params 接口参数  array('content'=>'test');
	 * @param $method 请求方式 POST|GET
	 * @param $multi 图片信息
	 * @return string
	 */
	public function api($command, $access_token, $openid, $params=array(), $method = 'GET', $multi = false) {
		// 请求参数（公共部分）
		$params['oauth_consumer_key']	= $this->akey;
		$params['access_token']		= $access_token;
		$params['openid']			= $openid;
		$params['clientip']			= $this->getClientIp();
		$params['oauth_version']		= '2.a';
		$params['scope']				=  'all';
		// 私有参数，因为全部都用json方式返回数据，所以直接写到这里了
		$params['format']			= 'json';
		// Tencent API 中的额外参数，这些参数应该不是必须的
		$params['appfrom']			= 'php-sdk2.0beta';
		$params['seqid']				= time();
		$params['serverip']			= $_SERVER['SERVER_ADDR'];
		
		$url = $this->api_url . trim($command, '/');
		//请求接口
		$r = $this->request($url, $params, $method, $multi);
		$r = preg_replace('/[^\x20-\xff]*/', "", $r);					//清除不可见字符
		$r = iconv("utf-8", "utf-8//ignore", $r);					//UTF-8转码，加上//ignore参数防止出错后的停止执行
		return $r;
	}
	/**
	 * 发起一个HTTP/HTTPS的请求
	 * @param $url 接口的URL 
	 * @param $params 接口参数   array('content'=>'test', 'format'=>'json');
	 * @param $method 请求类型    GET|POST
	 * @param $multi 图片信息
	 * @param $extheaders 扩展的包头信息
	 * @return string
	 */
	public function request( $url , $params = array(), $method = 'GET' , $multi = false, $extheaders = array()) {
		$method = strtoupper($method);
		$ci = curl_init();
		curl_setopt($ci, CURLOPT_USERAGENT, 'PHP-SDK OAuth2.0');
		curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, 3);
		curl_setopt($ci, CURLOPT_TIMEOUT, 3);
		curl_setopt($ci, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, false);
		curl_setopt($ci, CURLOPT_SSL_VERIFYHOST, false);
		curl_setopt($ci, CURLOPT_HEADER, false);
		$headers = (array)$extheaders;
		switch ($method) {
			case 'POST':
				curl_setopt($ci, CURLOPT_POST, TRUE);
				if (!empty($params)) {
					if($multi) {
						foreach($multi as $key => $file) {
							$params[$key] = '@' . $file;
						}
						curl_setopt($ci, CURLOPT_POSTFIELDS, $params);
						$headers[] = 'Expect: ';
					} else {
						curl_setopt($ci, CURLOPT_POSTFIELDS, http_build_query($params));
					}
				}
				break;
			case 'DELETE':
			case 'GET':
				$method == 'DELETE' && curl_setopt($ci, CURLOPT_CUSTOMREQUEST, 'DELETE');
				if (!empty($params)) {
					$url = $url . (strpos($url, '?') ? '&' : '?') . (is_array($params) ? http_build_query($params) : $params);
				}
				break;
		}
		curl_setopt($ci, CURLINFO_HEADER_OUT, TRUE );
		curl_setopt($ci, CURLOPT_URL, $url);
		if($headers) {
			curl_setopt($ci, CURLOPT_HTTPHEADER, $headers );
		}
		$response = curl_exec($ci);
		curl_close ($ci);
		return $response;
	}
	/**
	 * 获取客户端的ip
	 * @return String
	 */
	public function getClientIp() {
		if (getenv ( "HTTP_CLIENT_IP" ) && strcasecmp ( getenv ( "HTTP_CLIENT_IP" ), "unknown" ))
			$ip = getenv ( "HTTP_CLIENT_IP" );
		else if (getenv ( "HTTP_X_FORWARDED_FOR" ) && strcasecmp ( getenv ( "HTTP_X_FORWARDED_FOR" ), "unknown" ))
			$ip = getenv ( "HTTP_X_FORWARDED_FOR" );
		else if (getenv ( "REMOTE_ADDR" ) && strcasecmp ( getenv ( "REMOTE_ADDR" ), "unknown" ))
			$ip = getenv ( "REMOTE_ADDR" );
		else if (isset ( $_SERVER ['REMOTE_ADDR'] ) && $_SERVER ['REMOTE_ADDR'] && strcasecmp ( $_SERVER ['REMOTE_ADDR'], "unknown" ))
			$ip = $_SERVER ['REMOTE_ADDR'];
		else
			$ip = "unknown";
		return ($ip);
	}
	/**
	 * 生成签名
	 * @param string    $method 请求方法 "get" or "post"
	 * @param string    $url_path 
	 * @param array     $params 表单参数
	 * @param string    $secret 密钥
	 */
	public static function makeSig($method, $url_path, $params, $secret) {
		$mk = self::makeSource ( $method, $url_path, $params );
		$my_sign = hash_hmac ( "sha1", $mk, strtr ( $secret, '-_', '+/' ), true );
		$my_sign = base64_encode ( $my_sign );
		return $my_sign;
	}

	private static function makeSource($method, $url_path, $params) {
		ksort ( $params );
		$strs = strtoupper($method) . '&' . rawurlencode ( $url_path ) . '&';
		$str = ""; 
		foreach ( $params as $key => $val ) { 
			$str .= "$key=$val&";
		}   
		$strc = substr ( $str, 0, strlen ( $str ) - 1 );
		return $strs . rawurlencode ( $strc );
	}
}
?>
